package cn.thoughtworks.school.controllers;

import cn.thoughtworks.school.annotations.Auth;
import cn.thoughtworks.school.entities.SubjectiveQuiz;
import cn.thoughtworks.school.entities.SubjectiveQuizSubmission;
import cn.thoughtworks.school.entities.User;
import cn.thoughtworks.school.handlers.BusinessException;
import cn.thoughtworks.school.repositories.SubjectiveQuizRepository;
import cn.thoughtworks.school.repositories.SubjectiveQuizSubmissionRepository;
import cn.thoughtworks.school.services.SubjectiveQuizService;
import cn.thoughtworks.school.services.Utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@RestController
@RequestMapping(value = "/api/v3/subjectiveQuizzes")
@Slf4j
public class SubjectiveQuizController {
  @Autowired
  private SubjectiveQuizRepository subjectiveQuizRepository;
  @Autowired
  private SubjectiveQuizSubmissionRepository subjectiveQuizSubmissionRepository;
  @Autowired
  private SubjectiveQuizService subjectiveQuizService;
  @Autowired
  private Utils utils;

  @RequestMapping(value = "", method = RequestMethod.POST)
  @Transactional
  public ResponseEntity addSubjectiveQuiz(@RequestBody Map data, @Auth User current) {
    SubjectiveQuiz subjectiveQuiz = subjectiveQuizService.createSubjectiveQuiz(data, current);

    Map<String, Object> body = new HashMap<>();
    body.put("uri", "/api/v3/subjectiveQuizzes/" + subjectiveQuiz.getId());
    body.put("id", subjectiveQuiz.getId());
    return new ResponseEntity<>(body, HttpStatus.CREATED);
  }

  @GetMapping(value = "pageable")
  public ResponseEntity getSubjectiveQuizzesPageable(@PageableDefault(sort = {"id"}, direction = Sort.Direction.DESC) Pageable pageable,
                                                     @RequestParam(required = false) Long quizId,
                                                     @RequestParam(required = false) Long userId,
                                                     @RequestParam(required = false) String description,
                                                     @RequestParam(required = false) List<String> tags,
                                                     @Auth User user) {
    Page subjectiveQuizzesPage = subjectiveQuizService.getSubjectiveQuizzesPageable(pageable,quizId,userId,description,tags,user);
    return new ResponseEntity<>(subjectiveQuizzesPage, HttpStatus.OK);
  }

  @RequestMapping(value = "/{quizId}", method = RequestMethod.GET)
  public ResponseEntity getSubjectiveQuiz(@PathVariable Long quizId) throws BusinessException {
    log.info(String.format("GET:  /api/v3/subjectiveQuizzes/%s", quizId));

    SubjectiveQuiz subjectiveQuiz = subjectiveQuizRepository
      .findByIdAndIsAvailableIsTrue(quizId)
      .orElseThrow(() -> new BusinessException(
        String.format("Unknown subjectiveQuiz with id: %s", quizId)
      ));

    ObjectMapper oMapper = new ObjectMapper();
    Map data = oMapper.convertValue(subjectiveQuiz, Map.class);

    return new ResponseEntity<>(data, HttpStatus.OK);
  }

  @RequestMapping(value = "/{quizId}", method = RequestMethod.PUT)
  @Transactional
  public ResponseEntity editSubjectiveQuiz(@PathVariable Long quizId, @RequestBody Map data, @Auth User current) throws BusinessException {

    SubjectiveQuiz subjectiveQuiz = subjectiveQuizRepository
      .findById(quizId)
      .orElseThrow(() -> new BusinessException(
        String.format("Unknown subjectiveQuiz with id: %s", quizId)
      ));

    subjectiveQuiz.setDescription((String) data.get("description"));
    subjectiveQuiz.setReferenceAnswer((String) data.get("referenceAnswer"));
    subjectiveQuiz.setUpdateTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    subjectiveQuiz.setTags(utils.formatTags((List<String>) data.get("tags"), current.getId()));
    subjectiveQuiz.setRemark((String) data.get("remark"));

    subjectiveQuizRepository.save(subjectiveQuiz);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }

  @RequestMapping(value = "/{quizId}", method = RequestMethod.DELETE)
  public ResponseEntity deleteSubjectiveQuiz(@PathVariable Long quizId) throws BusinessException {

    SubjectiveQuiz subjectiveQuiz = subjectiveQuizRepository
      .findById(quizId)
      .orElseThrow(() -> new BusinessException(
        String.format("Unknown subjectiveQuiz with id: %s", quizId)
      ));

    subjectiveQuiz.setIsAvailable(false);

    subjectiveQuizRepository.save(subjectiveQuiz);
    return new ResponseEntity<>(HttpStatus.NO_CONTENT);
  }


  @RequestMapping(value = "/selecting/{ids}", method = RequestMethod.GET)
  public ResponseEntity getSubjectiveQuizzes(@PathVariable String ids) {
    List<Long> quizIds = Stream.of(ids.split(",")).map(Long::parseLong).collect(Collectors.toList());
    List<SubjectiveQuiz> subjectiveQuizzes = subjectiveQuizRepository.findAllByIsAvailableIsTrueAndIdIn(quizIds);
    return new ResponseEntity<>(utils.formatList(subjectiveQuizzes), HttpStatus.OK);
  }

  @RequestMapping(value = "/students/{studentId}/assignments/{assignmentId}/quizzes/{ids}", method = RequestMethod.GET)
  public ResponseEntity getSubjectiveQuizzesInfo(@PathVariable Long studentId, @PathVariable Long assignmentId, @PathVariable String ids) throws BusinessException {
    List<Long> quizIds = Stream.of(ids.split(",")).map(Long::parseLong).collect(Collectors.toList());
    List<SubjectiveQuiz> subjectiveQuizzes = subjectiveQuizRepository.findAllByIsAvailableIsTrueAndIdIn(quizIds);
    List<SubjectiveQuizSubmission> quizSubmissions = subjectiveQuizSubmissionRepository.findByQuizIdInAndAssignmentIdAndUserIdOrderByIdDesc(quizIds, assignmentId, studentId);
    List subjectiveQuizzesInfo = subjectiveQuizzes.stream()
      .map((subjectiveQuiz -> utils.addUserAnswer(subjectiveQuiz, quizSubmissions)))
      .collect(Collectors.toList());
    return new ResponseEntity<>(subjectiveQuizzesInfo, HttpStatus.OK);
  }

  @RequestMapping(value = "/students/{studentId}/assignments/{assignmentId}/quizzes/{quizId}", method = RequestMethod.POST)
  public ResponseEntity submitAnswer(@PathVariable Long studentId, @PathVariable Long assignmentId, @PathVariable Long quizId, @RequestBody Map map) throws BusinessException {

    SubjectiveQuizSubmission quizSubmission = new SubjectiveQuizSubmission(assignmentId, quizId, studentId, (String) map.get("userAnswer"));
    quizSubmission.setSubmitTime(new SimpleDateFormat("YYYY-MM-dd HH-mm-ss").format(new Date()));
    subjectiveQuizSubmissionRepository.save(quizSubmission);
    return new ResponseEntity<>(HttpStatus.CREATED);
  }

  @RequestMapping(value = "/oldAssignments/{oldAssignmentId}/quizzes/{quizId}/newAssignments/{newAssignmentId}", method = RequestMethod.GET)
  public ResponseEntity subjectiveQuizDataMigration(@PathVariable Long oldAssignmentId, @PathVariable Long quizId, @PathVariable Long newAssignmentId) throws BusinessException {
    List<SubjectiveQuizSubmission> subjectiveQuizSubmissions = subjectiveQuizSubmissionRepository.findAllByAssignmentIdAndQuizId(oldAssignmentId, quizId);
    if (subjectiveQuizSubmissions.size() != 0) {
      for (SubjectiveQuizSubmission subjectiveQuizSubmission : subjectiveQuizSubmissions) {
        subjectiveQuizSubmission.setAssignmentId(newAssignmentId);
        subjectiveQuizSubmissionRepository.save(subjectiveQuizSubmission);
      }
    }

    return new ResponseEntity<>(HttpStatus.OK);
  }
}
